
package lex;

%%

%class Scanner
%type Token
%column
%line

%{
  private StringBuffer string = new StringBuffer();
  private int string_line, string_column;
  private int comment_line, comment_column;

  private Token tok(String tag, String value, int line, int column) {
    return new Token(tag, value, line+1, column+1);
  }

  private Token tok(String tag, String value) { 
    return tok(tag, value, yyline, yycolumn);
  }

  private Token tok(String tag) {
    return tok(tag, yytext());
  }

  private Token tok() {
    return tok(yytext());
  }

  private Token err(String message, int line, int column) {
    yybegin(ERROR);
    return tok("ERROR", message, line, column);
  }

  private Token err(String message) {
    return err(message, yyline, yycolumn);
  }

%}

NEWLINE     = \r\n|[\r\n]
WHITESPACE  = [ \t\r\n]

ID          = [a-z][a-zA-Z0-9_]*
CLASS_ID    = [A-Z][a-zA-Z0-9_]*
INTEGER     = 0|[1-9][0-9]*

/*
INT32       = -?[0-9]{1,9}|           
             -?1[0-9]{9}|
             -?20[0-9]{8}|
             -?21[0-3][0-9]{7}|
             -?214[0-6][0-9]{6}|
             -?2147[0-3][0-9]{5}|
             -?21474[0-7][0-9]{4}|
             -?214748[012][0-9]{4}|
             -?2147483[0-5][0-9]{3}|
             -?21474836[0-3][0-9]{2}|
               214748364[0-7]|
              -214748364[0-8]
*/

LINECOMMENT = \/\/[^\r\n]*{NEWLINE} 

KEYWORDS    = class|extends|static|void|int|boolean|string|return|if|else|while|break|continue|this|new|length|true|false|null

OPERATORS   = [\[\]\(\)\.\-!\*\/\%\+\<\>\=]|[\<\>\=!]\=|&&|\|\|

%state STRING
%state COMMENT
%state ERROR

%%

<YYINITIAL> {
  /* syntax identifiers */
  {KEYWORDS}                     { return tok(); }
  {OPERATORS}                    { return tok(); }
  [;,\{\}]                       { return tok(); }

  /* identifiers */ 
  {ID}                           { return tok("ID"); }
  {CLASS_ID}                     { return tok("CLASS_ID"); }
 
  /* literals */
  {INTEGER}                      { return tok("INTEGER"); }
  \"                             { string.setLength(0);
                                   string.append('\"');
                                   string_line = yyline;
                                   string_column = yycolumn;
                                   yybegin(STRING); }

  /* comments */
  {LINECOMMENT}                  { /* ignore */ }
  \/\*                           { yybegin(COMMENT);
                                   comment_line = yyline;
                                   comment_column = yycolumn; }
 
  /* whitespace */
  {WHITESPACE}                   { /* ignore */ }


  [\:\$\&]                       { return err("lexical error; invalid character '" + yytext() + "'"); }
  _                              { return err("lexical error; an identifier cannot start with '_'"); }
  0+[0-9]+                       { return err("lexical error; numbers should not have leading zeros"); }
}

<STRING> {
  \"                             { yybegin(YYINITIAL); 
                                   string.append('\"');
                                   return tok("STRING", string.toString(), string_line, string_column); }
  [^\t\r\n\"\\]+                 { string.append(yytext()); }
  \\[trn\"\\]                    { string.append(yytext()); }

  (\\[^trn])|[\t\r\n]            { return err("lexical error; malformed string literal", string_line, string_column); }
  <<EOF>>                        { return err("lexical error; malformed string literal", string_line, string_column); }
}

<COMMENT> {
  \*\/                           { yybegin(YYINITIAL); }
  [^\*]*                         { /* ignore */ }
  \*                             { /* ignore */ }

  <<EOF>>                        { return err("lexical error; unterminated comment", comment_line, comment_column); }
}

/* error fallback */

.                                { throw new Error("Lexical error"); }

